package com.lib.okhttp;

import android.content.Context;
import android.util.Log;

import com.lib.utils.CloseUtils;
import com.lib.utils.XLog;

import java.io.InputStream;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class SSLConfig {
    private static final String HOST_NAME = "39.98.52.222";//请求服务器的主机名称
    private static String CERTIFICATE_NAME = "tech_public.crt";//下载的证书的文件名称
    //证书
    private X509Certificate mX509Certificate;
    //需要配置给ok的SSLSocketFactory
    private SSLSocketFactory mSslSocketFactory;

    //证书管理者
    private MyTrustManager mTrustManager;

    private HostnameVerifier mHostnameVerifier = new InnerHostnameVerifier();

    private static SSLConfig instance;

    private SSLConfig() {

        mX509Certificate = readCert(XLog.sContext, CERTIFICATE_NAME);
        try {
            SSLContext mSSLContext = SSLContext.getInstance("TLS");
            //从assets文件夹下根据证书名字读取证书,变成一个可用的证书对象
            //校验服务端和本地证书是否一致
            mTrustManager = new MyTrustManager(mX509Certificate);
            //初始化必要的对象,固定格式直接使用即可
            mSSLContext.init(null, new TrustManager[]{mTrustManager}, new SecureRandom());
            mSslSocketFactory = mSSLContext.getSocketFactory();
        } catch (Exception e) {
            XLog.w(Log.getStackTraceString(e));
        }
    }


    public static SSLConfig getInstance() {

        if (instance == null) {
            instance = new SSLConfig();
        }
        return instance;
    }

    /**
     * 根据asset下证书的名字取出证书,然后变成流,在变成证书对象
     */
    private static X509Certificate readCert(Context context, String assetName) {

        InputStream in;
        try {
            in = context.getAssets().open(assetName);
        } catch (Exception e) {
            XLog.w(e.getMessage());
            return null;
        }
        X509Certificate cert = null;
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            cert = (X509Certificate) cf.generateCertificate(in);
        } catch (Exception e) {
            XLog.w(e.getMessage());
        } finally {
            CloseUtils.close(in);
        }
        return cert;
    }

    public HostnameVerifier getHostnameVerifier() {
        return mHostnameVerifier;
    }

    public SSLSocketFactory getSslSocketFactory() {

        return mSslSocketFactory;
    }

    public MyTrustManager getTrustManager() {

        return mTrustManager;
    }

    /**
     * 实现了 X509TrustManager
     * 通过此类中的 checkServerTrusted 方法来确认服务器证书是否正确
     */
    private static final class MyTrustManager implements X509TrustManager {

        X509Certificate cert;

        MyTrustManager(X509Certificate cert) {

            this.cert = cert;
        }

        @Override// 我们在客户端只做服务器端证书校验。
        public void checkClientTrusted(X509Certificate[] chain, String authType) {

        }

        /**
         * @param chain 服务端返回的证书数组,因为服务器可能有多个https证书,我们在这里的
         *              逻辑就是拿到第一个证书,然后和本地证书判断,如果不一致则抛出异常!!!
         */
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//             确认服务器端证书和代码中 hard code 的 CRT 证书相同。
//            这里因为我们服务器只有一个证书,没有遍历,如果有多个,这里是for循环取出挨个判断
//            if (chain[0].equals(this.cert)) {
//                return;
//            }
//            throw new CertificateException("未知证书!");
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {

            return new X509Certificate[]{};
        }
    }

    public static final class InnerHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
//            XLog.i("hostname = " + hostname);
//            return hostname.equals(HOST_NAME);
            return true;
        }
    }
}